package com.example.compiler;

import com.example.annotation.ARouter;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Type;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * 自定义注解处理器，处理注解
 */
@AutoService(Processor.class)//通过AutoService用于注册
@SupportedAnnotationTypes("com.example.annotation.ARouter")//处理注解对象
@SupportedSourceVersion(SourceVersion.RELEASE_8)//jdk 版本
@SupportedOptions("content")//处理器接受参数（处理器为java项目，不能直接接受android项目的参数，通过该注解接受）
public class ArouterProcessor extends AbstractProcessor {
    //操作Elements工具类
    private Elements elementUtils;
    //类信息工具类
    private Types typeUtils;
    //用来输出日志
    private Messager messager;
    //文件生成器（生成类文件等）
    private Filer filer;

    /**
     * 初始化方法
     *
     * @param processingEnv
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
        typeUtils = processingEnv.getTypeUtils();
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();
        String content = processingEnv.getOptions().get("content");
        messager.printMessage(Diagnostic.Kind.NOTE, content);
    }

    /**
     * 这个方法相当于main函数，开始处理注解，
     * 注解处理器的核心方法，例如可以在这里生成java文件
     *
     * @param sets     使用了支持处理注解的节点集合（例如类上面写了注解，方法上面写了注解等等）
     * @param roundEnv 运行环境，可以通过该对象查找到，找到注解。
     * @return true表示处理完成，false表示处理失败
     */
    @Override
    public boolean process(Set<? extends TypeElement> sets, RoundEnvironment roundEnv) {
        if (sets.isEmpty()) {
            return false;
        }
        //获取项目中所有使用了com.example.annotation.ARouter注解的节点。(如app的 mainactivity)
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ARouter.class);
        //遍历用了该注解的所有的节点
        for (Element element :
                elements) {
            //类节点之上的节点，就是包节点(获取包节点)
           String packageName =  elementUtils.getPackageOf(element).getQualifiedName().toString();
           String className = element.getSimpleName().toString();
           messager.printMessage(Diagnostic.Kind.NOTE,"被注解的类有:"+className);
           //最终我们想要生成的类名
            String finalClasName = className+"$$ARouter";

            //通过该节点，获取注解的path值
            ARouter aRouter = element.getAnnotation(ARouter.class);
            String path = aRouter.path();

            messager.printMessage(Diagnostic.Kind.NOTE,"path:"+path);

        //方法
            MethodSpec findTargetClass = MethodSpec
                    //方法名
                    .methodBuilder("findTargetClass")
                    //修饰符
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    //返回值
                    .returns(Class.class)
                    //参数
                    .addParameter(String.class, "path")
                    //方法体 $S字符串
                    //$T 类
                  //  .addStatement("return path.equalsIgnoreCase($S) ? $T.class:null",path, ClassName.get((Type) element))

                    .addStatement("return path.equalsIgnoreCase($S)?$T.class:null",path,ClassName.get((TypeElement) element))
//
//                    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                    .build();
            //类名
            TypeSpec helloWorld = TypeSpec.classBuilder(finalClasName)
                    //类 修饰符
                    .addModifiers(Modifier.PUBLIC)
                    //添加方法
                    .addMethod(findTargetClass)
                    .build();
            //引入包
            JavaFile javaFile = JavaFile.builder(packageName, helloWorld)
                    .build();

            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        return false;
    }
}
